package com.heima.controller;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 陈辉
 * @data 2023 16:22
 */
//卖票任务类  -- 线程任务
public class SellTicket implements Runnable {
    private int ticket = 100;

    public void run() {
        while (true) {
            synchronized ("abc") {
                if (ticket <= 0) {
                    break;
                }

                try {
                    Thread.sleep(100);          //模拟出票耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
                System.out.println(Thread.currentThread().getName() + "正在卖票，还剩：" + ticket + "张票");
            }
        }
    }
}

/*
出现多线程数据安全问题的核心原因？
    1. 多线程环境
    2. 有临界资源： 共享数据
    3. 有多条语句操作临界资源

到底会产生哪些问题？
    1. 可见性问题       -- 多线程环境下，主内存数据发生改变，对于线程变量副本不可见!
                            解决：用volatile修饰共享变量，加锁！
    2. 原子性问题        -- 多条语句要么同时执行，要么都不执行，不能执行到一半不执行，然后别的线程抢到cpu来执行
                          --加锁： 使用synchronized悲观锁，可以使用乐观锁。
    3. 指令重排          -- 不可演示，因为该问题发生在字节码编译类加载期,解决：加锁

 */

class Money{
    //小明和小红的共有结婚基金
    //volatile关键字特点：被volatile修饰的变量，未来每次访问都会强制清空变量副本
    //    public static volatile Integer money = 100000;
    public static AtomicInteger count = new AtomicInteger(0);
//    public static Integer count = 0;
    public static Integer money = 100000;
}

class Demo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(100);
        //原子性问题演示
        for (int j = 1; j <= 100; j++) {
            new Thread(()->{
                for (int i = 1; i <= 100 ; i++) {
                    // count++不具备原子性！！！
                    /*
                        第一步： 取出count变量当前值
                        第二步:  在当前值基础上 + 1
                        第三步： 将变更后的结果赋值给count
                     */
//                    Money.count++;
                    Money.count.getAndIncrement();
                }
                latch.countDown();
            }).start();
        }

        latch.await();
        System.out.println(Money.count);

    }

    //可见性问题分析
    private static void canSee() {
        //线程2  -- 小明
        new Thread(() -> {
            try {
                Thread.sleep(100);

                //偷偷花1万买皮肤
                Money.money -= 90000;
                System.out.println("小明偷偷花了9万买了赵信皮肤...还剩：" + Money.money);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        //线程1  -- 小红
        new Thread(()->{
            while(true){
                synchronized ("abc") {
                    if (Money.money != 100000){   //在锁的内部访问共享变量，每次访问之前都会强制清空变量副本
                        break;
                    }
                }
            }

            System.out.println("结婚基金已经不是十万了");
        }).start();
    }

    //指令重排
    private static void zlcp() {
        int a = 10;
        a++;
        int b = a;
        if (b > 10) {
            System.out.println("hello");
        } else {
            System.out.println("world");
        }
    }

    private static void sellTicket() {
        //创建卖票任务
        SellTicket st = new SellTicket();

        //创建三个线程同时卖票，期望：三个窗口卖同100张票，保证数据安全
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        Thread t3 = new Thread(st);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}